﻿///
/// Authors: Lee Gary 
///          Wu Si Hui (TrustAllCertificatePolicy) class
///
/// Date: 24/09/2010
///
/// Purpose: 
///
/// Wrapper class for all communication to SharePoint via it's webservices
/// The core/brain of the remote invocation
/// 
/// Future:
/// 1.) Modify CreateFolder() to handle SharePoint results
/// 2.) Proper exception handling and logging
/// 
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Web.Services.Protocols;
using System.Xml;
using GSPMigratorCore.GSPCopyWS;
using GSPMigratorCore.GSPDocLibWS;
using GSPMigratorCore.GSPListWS;
using GSPMigratorCore.GSPObjects;
using GSPMigratorCore.GSPSiteDataWS;
using GSPMigratorCore.Linq2XML;
using Microsoft.SharePoint;
using GSPMigratorCore.Misc; //for SPFileSystemObjectType only, no dependencies on dll

namespace GSPMigratorCore.Helper
{
    /// <summary>
    /// for https
    /// </summary>
    public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
    {
        public TrustAllCertificatePolicy()
        { }

        public bool CheckValidationResult(ServicePoint sp,
         X509Certificate cert, WebRequest req, int problem)
        {
            return true;
        }
    }

    public class WebserviceHelper
    {
        private string spURL = string.Empty;
        private bool isFBAEnabled = false;
        private string spAdminId = string.Empty;
        private string spAdminPw = string.Empty;
        private string spAdminDomain = string.Empty;

        /// <summary>
        /// default constructor
        /// </summary>
        public WebserviceHelper()
        { }

        /// <summary>
        /// Webservice helper class 
        /// </summary>
        /// <param name="_url">base url e.g. (http://mysharepointserver:80/site/testsite)</param>
        /// <param name="_spAdminId">User id used to connect to SharePoint web service</param>
        /// <param name="_spAdminPw">Password of the specified account</param>
        /// <param name="_spAdminDomain">Domain of the user</param>
        /// <param name="_isFBAEnabled">Form-based Authentication enabled indicator</param>
        public WebserviceHelper(string _url, string _spAdminId, string _spAdminPw, string _spAdminDomain, bool _isFBAEnabled)
        {
            this.spURL = _url;
            this.spAdminId = _spAdminId;
            this.spAdminPw = _spAdminPw;
            this.spAdminDomain = _spAdminDomain;
            this.isFBAEnabled = _isFBAEnabled;
        }

        #region authentication

        internal CookieContainer AuthenticateFBA()
        {
            CookieContainer cookies = new CookieContainer();
            using (GSPAuthenticationWS.Authentication authSvc = new GSPAuthenticationWS.Authentication())
            {
                authSvc.Url = this.spURL + "/_vti_bin/authentication.asmx";
                authSvc.CookieContainer = new System.Net.CookieContainer();     //create a new cookie container
                authSvc.AllowAutoRedirect = true;

                //set the FBA login information
                try
                {
                    GSPAuthenticationWS.LoginResult result = authSvc.Login(this.spAdminId, this.spAdminPw);
                    if (result.ErrorCode == GSPAuthenticationWS.LoginErrorCode.NoError)
                    {
                        CookieCollection cookieColl = authSvc.CookieContainer.GetCookies(new Uri(authSvc.Url));
                        //Get the specific cookie which contains the security token
                        cookies.Add(cookieColl[result.CookieName]);
                    }
                }
                catch (WebException wex)
                {
                    throw new NotImplementedException("Specified Sharepoint server is not FBA-enabled");
                }
                return cookies;
            }
        }

        internal NetworkCredential AuthenticateIWA()
        {
            return new NetworkCredential(this.spAdminId, this.spAdminPw, this.spAdminDomain);
        }


        /// <summary>
        /// Helper class to authenticate current user against SharePoint, no need to 
        /// hardcode/use default credentials, allows flexibility to switch between FBA/IWA
        /// </summary>
        /// <param name="SoapProtocol"></param>
        private void Authenticate(SoapHttpClientProtocol SoapProtocol)
        {
            if (this.isFBAEnabled)
            {
                SoapProtocol.CookieContainer = AuthenticateFBA();
            }
            else
            {
                ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();
                ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
                {
                    //Here we can write code for implementing client side validation.
                    // This anonymous function is a call back function which is called once the SSL validation completes on WebServer.
                    return true;
                };
                SoapProtocol.Credentials = AuthenticateIWA();
            }
        }
        #endregion

        #region From File share to SharePoint
        /// <summary>
        /// Basic consumption of SharePoint Webservice 
        /// </summary>
        /// <param name="parentFolderSlashChildFolder"></param>
        /// <returns>Success or failure</returns>
        public string CreateFolder(string parentFolderSlashChildFolder)
        {
            string msg = string.Empty;
            using (Dws SPDocLibWS = new Dws())
            {
                SPDocLibWS.Url = this.spURL + "/_vti_bin/dws.asmx";
                Authenticate(SPDocLibWS);
                msg = SPDocLibWS.CreateFolder(parentFolderSlashChildFolder);
            }
            return msg;
        }
        
        /// <summary>
        /// Basic consumption of web service (copy.asmx)
        /// </summary>
        /// <param name="sourceFilePath"></param>
        /// <param name="destinationUrl"></param>
        /// <returns>Success/failure</returns>
        public string UploadFile(string sourceFilePath, string destinationUrl)
        {
            string msg = string.Empty;
            Copy c = new Copy();
            c.Timeout = 600000;
            c.Url = this.spURL + "/_vti_bin/copy.asmx";
            Authenticate(c);

            byte[] myBinary = File.ReadAllBytes(sourceFilePath);
            if (myBinary.Length == 0)
            { 
                throw new GSPException(1, "FileSystem", "FS001", "Empty file cannot be uploaded to SharePoint");
            }
            string[] destinationUrlColl = { destinationUrl };

            FieldInformation info1 = new FieldInformation(); 
            info1.DisplayName = "Title"; 
            info1.InternalName = "Title"; 
            info1.Type = FieldType.Text; 
            info1.Value = "new title"; 

            FieldInformation[] info = { info1 }; 
            CopyResult resultTest = new CopyResult(); 
            CopyResult[] result = { resultTest }; 

            try 
            { 
                //When creating new content use the same URL in the SourceURI as in the Destination URL argument
                 c.CopyIntoItems(destinationUrl, destinationUrlColl, info, myBinary, out result); 
                 if (result[0].ErrorMessage != null)
                 {
                     msg = "Error: " + result[0].ErrorMessage;
                 }
                 else
                 {
                     msg = "Success";
                 }
            } 
            catch (Exception ex) 
            { 
                //TODO: logging & handling
            }
            return msg;
        }

        #endregion

        #region From SharePoint to Application

        ///// <summary>
        ///// List of GSPFolder objects, the core behind it is in GSPLinQtoXMLHelper class which 
        ///// makes use of LinQ to parse the XML and returning a list of GSP customised objects
        ///// automatically. A lot experimentation done via U2U caml builder :)
        ///// </summary>
        ///// <param name="DocLibName"></param>
        ///// <param name="limit">limiting how many records to retrieve from SharePoint</param>
        ///// <param name="siteSlashParentFolder"></param>
        ///// <returns></returns>
        //public List<GSPFolder> GetAllFolders(string DocLibName, string limit, string siteSlashParentFolder)
        //{
        //    List<GSPFolder> GSPFolders = new List<GSPFolder>();

        //    #region webservice
        //    Lists SPListWS = new Lists();
        //    SPListWS.Url = this.spURL + @"/_vti_bin/lists.asmx";
        //    Authenticate(SPListWS); //authenticate
        //    #endregion

        //    #region XML obj
        //    XmlNode ndListItems = null;
        //    XmlDocument xDoc = new XmlDocument();
        //    XmlNode ndQuery = xDoc.CreateNode(XmlNodeType.Element, "Query", "");
        //    XmlNode ndViewFields = xDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
        //    XmlNode ndQueryOptions = xDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");
        //    #endregion

        //    #region caml
        //    ndQuery.InnerXml = "<Where>" +
        //                           "<And>" +
        //                               "<Eq>" +
        //                                    "<FieldRef Name='FSObjType' /> " +
        //                                    "<Value Type='Lookup'>" + (int)SPFileSystemObjectType.Folder + "</Value>" +
        //                               "</Eq>" +
        //                               "<Eq>" +
        //                                    "<FieldRef Name='FileDirRef' />" +
        //                                    "<Value Type='Lookup'>" + siteSlashParentFolder + "</Value>" +
        //                               "</Eq>" +
        //                            "</And>" +
        //                        "</Where>";
        //    ndViewFields.InnerXml = "";
        //    ndQueryOptions.InnerXml = "<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>" +
        //                              "<ViewAttributes Scope='RecursiveAll'/>";//"<ViewAttributes Scope='RecursiveAll'  />";
        //    #endregion
        //    try
        //    {
        //        ndListItems = SPListWS.GetListItems(
        //                            DocLibName, //List Name (doc lib = lists)
        //                            "", //View Name (use default or no view)
        //                            ndQuery, //CAML Query node
        //                            ndViewFields, //CAML View fields
        //                            limit, // limit results set as 100k
        //                            ndQueryOptions, //query options
        //                            GetSiteID().ToString()); // SPWeb ID 
        //    }
        //    catch (Exception ex)
        //    {
        //        //TODO: log
        //    }

        //    if (ndListItems != null)
        //    {
        //        GSPLinQtoXMLHelper xmlHelper = new GSPLinQtoXMLHelper();
        //        GSPFolders = xmlHelper.ParseFoldersXML(ndListItems);
        //    }
        //    return GSPFolders;
        //}

        /// <summary>
        /// Same as GetAllFolders
        /// </summary>
        /// <param name="hideDefaultDocLib"></param>
        /// <returns></returns>
        public List<GSPDocLib> GetAllDocLib(bool hideDefaultDocLib)
        {
            List<GSPDocLib> test = new List<GSPDocLib>();
            Lists SPListWS = new Lists();
            SPListWS.Url = this.spURL + @"/_vti_bin/Lists.asmx";
            Authenticate(SPListWS);

            try
            {
                //get list of all lists
                XmlNode ndListItems = SPListWS.GetListCollection();
                if (ndListItems != null)
                {
                    GSPLinQtoXMLHelper xmlHelper = new GSPLinQtoXMLHelper();
                    test = xmlHelper.ParseDocLibXML(ndListItems, hideDefaultDocLib);
                }
            }
            catch (WebException ex)
            {
                return null;
            }
            return test;
        }

        /// <summary>
        /// Lazy method to retrieve a list of all items in current List (Document library)
        /// if folder is specified (contains a '/'), it will scope only to the folder, 
        /// or else it will just search the root (the actual document library)
        /// </summary>
        /// <param name="documentLibrary"></param>
        /// <param name="folder"></param>
        /// <returns></returns>
        public List<GSPListItem> GetAllItemsInDocLib(string documentLibrary, string folder, out List<GSPFolder> GSPFolders)
        {
            GSPFolders = new List<GSPFolder>();
            List<GSPListItem> GSPListItems = new List<GSPListItem>();
            Lists SPListWS = new Lists();
            SPListWS.Url = this.spURL + @"/_vti_bin/Lists.asmx";
            Authenticate(SPListWS);
            
            XmlDocument xDoc = new XmlDocument();
            XmlNode ndQuery = xDoc.CreateNode(XmlNodeType.Element, "Query", "");
            XmlNode ndViewFields = xDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
            XmlNode ndQueryOptions = xDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");

            ndQuery.InnerXml = "";
            ndViewFields.InnerXml = "";
            if (!folder.Contains(GSPConstants.TreeView.LazyLoadedChild))
            {
                if (folder.Contains("/"))
                {
                    ndQueryOptions.InnerXml = "<Folder>" + folder + "</Folder>" +
                                              "<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>";
                }
            }
            else
            {
                ndQueryOptions.InnerXml = "<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>";
            }
            try
            {
                //get list of all items in folder
                XmlNode ndListItems = SPListWS.GetListItems(
                    documentLibrary,
                    "",
                    ndQuery,
                    ndViewFields,
                    null,
                    ndQueryOptions,
                    GetSiteID().ToString());

                if (ndListItems != null)
                {
                    GSPLinQtoXMLHelper xmlHelper = new GSPLinQtoXMLHelper();
                    GSPListItems = xmlHelper.ParseSPListItemsXML(ndListItems, out GSPFolders);
                }
            }
            catch (WebException ex)
            {
                GSPFolders = new List<GSPFolder>();
                return null;
            }
            return GSPListItems;
        }

        #endregion

        /// <summary>
        /// Easiest, best performance and most dynamic way to retrieve the web id
        /// </summary>
        /// <returns></returns>
        internal Guid GetSiteID()
        {
            Guid SiteGuid = Guid.Empty;
            try
            {
                SiteData site = new SiteData();
                site.Url = this.spURL + "/_vti_bin/sitedata.asmx";
                Authenticate(site);

                _sWebMetadata webMetaData;
                _sWebWithTime[] arrWebWithTime;
                _sListWithTime[] arrListWithTime;
                _sFPUrl[] arrUrls;
                string roles;
                string[] roleUsers;
                string[] roleGroups;
                
                uint i = site.GetWeb(out webMetaData, out arrWebWithTime, 
                    out arrListWithTime, out arrUrls, out roles, out roleUsers, out roleGroups);

                SiteGuid = new Guid(webMetaData.WebID);
            }
            catch (Exception)
            { }
            return SiteGuid;
        }
    }
}